package com.dc.toolkit.guava.concurrent;

import com.google.common.util.concurrent.RateLimiter;
import lombok.SneakyThrows;
import org.junit.Test;

import java.util.concurrent.TimeUnit;


/**
 * Description: Guava_RateLimiter 测试类
 *
 * 参考：https://zhuanlan.zhihu.com/p/60979444
 *
 * @author: gdc
 * @date: 2021/8/19
 * @version 1.0
 */
public class RateLimiterTest {


    /**
     * 创建一个限流器，设置美妙放置的令牌数是1个。
     * 返回的RateLimiter对象可以保证1秒内不会超过1个令牌，并且以固定的速率进行放置，达到平滑输出的效果
     *
     * output: 基本上都是 1s 执行一次，符合一秒发放 1 个令牌的设定。
     * get 1 tokens:0.995055s
     * get 1 tokens:0.999455s
     * get 1 tokens:0.999742s
     * get 1 tokens:0.98598s
     * get 1 tokens:0.999325s
     * get 1 tokens:1.000226s
     * get 1 tokens:0.984632s
     * get 1 tokens:0.998747s
     * get 1 tokens:0.999663s
     */
    @Test
    public void test_SmoothBursty(){
        RateLimiter limiter = RateLimiter.create(1);
        while (true) {
            System.out.println("get 1 tokens:" + limiter.acquire() + "s");
        }
    }

    /**
     * RateLimiter 使用令牌桶算法，会进行令牌的累积，如果获取令牌桶的频率比较低，则不会等待，直接获取令牌
     *
     * --------------------------------------------
     * get 1 tokens: 0.989565s
     * get 1 tokens: 0.0s
     * get 1 tokens: 0.0s
     * get 1 tokens: 0.999841s
     * get 1 tokens: 0.999907s
     * get 1 tokens: 0.997305s
     * end
     * --------------------------------------------
     */
    @SneakyThrows
    @Test
    public void test_SmoothBursty2(){
        RateLimiter limiter = RateLimiter.create(1);
        while (true) {
            System.out.println("--------------------------------------------");

            System.out.println("get 1 tokens: " + limiter.acquire() + "s");


            TimeUnit.SECONDS.sleep(2);

            System.out.println("get 1 tokens: " + limiter.acquire() + "s");
            System.out.println("get 1 tokens: " + limiter.acquire() + "s");
            System.out.println("get 1 tokens: " + limiter.acquire() + "s");
            System.out.println("get 1 tokens: " + limiter.acquire() + "s");
            System.out.println("get 1 tokens: " + limiter.acquire() + "s");
            System.out.println("end");
        }
    }

    /**
     * RateLimiter 用于会累积令牌，所以可以应对突发流量。
     * 下面的代码，有一个请求会直接请求5个令牌，但是用于此时令牌桶中有累积的令牌，足以快速响应。
     * RateLimiter在没有足够令牌发放时，采用滞后处理的方式，
     * 也就是前一个请求获取令牌所需等待的时间由下一个请求来承受，也就是代替前一个请求进行等待。
     *
     * --------------------------------------------
     * get 5 tokens: 0.194144s
     * get 1 tokens: 1.000201s       滞后效应，需要替前一个请求进行等待
     * get 1 tokens: 0.19315s
     * end
     * --------------------------------------------
     * get 5 tokens: 0.200224s
     * get 1 tokens: 0.9871s         滞后效应，需要替前一个请求进行等待
     * get 1 tokens: 0.200148s
     * end
     * --------------------------------------------
     * get 5 tokens: 0.186893s
     * get 1 tokens: 0.999973s       滞后效应，需要替前一个请求进行等待
     * get 1 tokens: 0.186602s
     * end
     * --------------------------------------------
     */
    @Test
    public void test_SmoothBursty3(){
        RateLimiter limiter = RateLimiter.create(5);
        while (true) {
            System.out.println("--------------------------------------------");
            System.out.println("get 5 tokens: " + limiter.acquire(5) + "s");
            System.out.println("get 1 tokens: " + limiter.acquire() + "s");
            System.out.println("get 1 tokens: " + limiter.acquire() + "s");
            System.out.println("end");
        }
    }


    /**
     * RateLimiter的 SmoothWarmingUp是带有预热期的平滑限流。
     *  它启动后会有一段预热期，逐步将分发频率提升到配置的速率。
     *  比如下面代码中的例子，创建一个平均分发令牌速率为2，预热期为5秒钟。
     *  由于设置了预热时间是5秒，令牌桶一开始并不会0.5秒发一个令牌，而是形成一个平滑线性下降的坡度，频率越来越高，
     *  在5秒钟之内达到原本设置的频率，以后就以固定的频率输出。
     *  这种功能适合系统刚启动需要一点时间来“热身”的场景。
     *
     * --------------------------------------------
     * get 1 tokens: 0.0s
     * get 1 tokens: 1.399371s
     * get 1 tokens: 1.199215s
     * get 1 tokens: 1.000046s
     * end
     * --------------------------------------------
     * get 1 tokens: 0.795624s
     * get 1 tokens: 0.599715s
     * get 1 tokens: 0.488663s
     * get 1 tokens: 0.499323s
     * end
     * --------------------------------------------
     */
    @Test
    public void test_SmoothWarmingUp(){
        RateLimiter limiter = RateLimiter.create(2, 5, TimeUnit.SECONDS);
        while (true) {
            System.out.println("--------------------------------------------");
            System.out.println("get 1 tokens: " + limiter.acquire() + "s");
            System.out.println("get 1 tokens: " + limiter.acquire() + "s");
            System.out.println("get 1 tokens: " + limiter.acquire() + "s");
            System.out.println("get 1 tokens: " + limiter.acquire() + "s");
            System.out.println("end");
        }
    }

}
